/************************************************************************************
**  
* @copyright (c) 2015-, Technology Co., LTD. All Right Reserved.
*
*************************************************************************************/
/**
* @file     duye_cap_http_parser.h
* @version     
* @brief      
* @author   
* @date     2013-04-13
* @note 
*
*  1. 2018-04-13 created this file
* 
*/
#pragma once

#include <string>
#include <duye_helper.h>
#include <duye_cap_tcp_session.h>

namespace duye {

/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
 * faster
 */
#ifndef HTTP_PARSER_STRICT
# define HTTP_PARSER_STRICT 1
#endif

/* Maximium header size allowed. If the macro is not defined
 * before including this header then the default is used. To
 * change the maximum header size, define the macro in the build
 * environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
 * the effective limit on the size of the header, define the macro
 * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
 */
#ifndef HTTP_MAX_HEADER_SIZE
# define HTTP_MAX_HEADER_SIZE (80*1024)
#endif

/* Request Methods */
typedef enum etag_HttpMethod {
	HTTP_DELETE,
	HTTP_GET,
	HTTP_HEAD,
	HTTP_POST,
	HTTP_PUT,
	/* pathological */ 
	HTTP_CONNECT,
	HTTP_OPTIONS,
	HTTP_TRACE,
	/* webdav */
	HTTP_COPY,
	HTTP_LOCK,
	HTTP_MKCOL,
	HTTP_MOVE,
	HTTP_PROPFIND,
	HTTP_PROPPATCH,
	HTTP_SEARCH,
	HTTP_UNLOCK,
	/* subversion */ 
	HTTP_REPORT,
	HTTP_MKACTIVITY,
	HTTP_CHECKOUT,
	HTTP_MERGE,
	/* upnp */
	HTTP_MSEARCH,
	HTTP_NOTIFY,
	HTTP_SUBSCRIBE,
	HTTP_UNSUBSCRIBE,
	/* RFC-5789 */ 
	HTTP_PATCH,
	HTTP_PURGE,
	/* CalDAV */
	HTTP_MKCALENDAR,
	/* unknown */
	HTTP_UNKNOWN
} HttpMethod;

/* http parser type */
typedef enum etag_HttpParserType {
	HPT_REQUEST, 
	HPT_RESPONSE, 
	HPT_BOTH
} HttpParserType;

/* Flag values for http parser flags field */
typedef enum etag_HttpParserFlag {
	HPF_CHUNKED               = 1 << 0,
	HPF_CONNECTION_KEEP_ALIVE = 1 << 1,
	HPF_CONNECTION_CLOSE      = 1 << 2,
	HPF_CONNECTION_UPGRADE    = 1 << 3,
	HPF_TRAILING              = 1 << 4,
	HPF_UPGRADE               = 1 << 5, 
	HPF_SKIPBODY              = 1 << 6
} HttpParserFlag;

/* http parser errno */
typedef enum etag_HttpParserErrno {
	/* No error */
	HPE_OK,
	/* Listener-related errors */
	HPE_L_MESSAGE_BEGIN,
	HPE_L_URL,
	HPE_L_HEADER_FIELD,
	HPE_L_HEADER_VALUE,
	HPE_L_HEADERS_COMPLETE,
	HPE_L_BODY,
	HPE_L_MESSAGE_COMPLETE,
	HPE_L_STATUS,
	/* Parsing-related errors */
	HPE_INVALID_EOF_STATE,
	HPE_HEADER_OVERFLOW,
	HPE_CLOSED_CONNECTION,
	HPE_INVALID_VERSION,
	HPE_INVALID_STATUS,
	HPE_INVALID_METHOD,
	HPE_INVALID_URL,
	HPE_INVALID_HOST,
	HPE_INVALID_PORT,
	HPE_INVALID_PATH,
	HPE_INVALID_QUERY_STRING,
	HPE_INVALID_FRAGMENT,
	HPE_LF_EXPECTED,
	HPE_INVALID_HEADER_TOKEN,
	HPE_INVALID_CONTENT_LENGTH,
	HPE_INVALID_CHUNK_SIZE,
	HPE_INVALID_CONSTANT,
	HPE_INVALID_INTERNAL_STATE,
	HPE_STRICT,
	HPE_PAUSED,
	HPE_UNKNOWN
} HttpParserErrno;

/* http parser url field */
typedef enum etag_HttpParserUrlField { 
	HPUF_SCHEMA           = 0,
	HPUF_HOST             = 1,
	HPUF_PORT             = 2,
	HPUF_PATH             = 3,
	HPUF_QUERY            = 4,
	HPUF_FRAGMENT         = 5,
	HPUF_USERINFO         = 6,
	HPUF_MAX              = 7
} HttpParserUrlField;

/* Result structure for http_parser_parse_url().
 *
 * Callers should index into field_data[] with UF_* values iff field_set
 * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
 * because we probably have padding left over), we convert any port to
 * a uint16_t.
 */
typedef struct tag_HttpParserUrl {
  uint16 fieldSet;            /* Bitmask of (1 << HPUF_*) values */
  uint16 port;                /* Converted HPUF_PORT string */

  struct {
    uint16 off;               /* Offset into buffer in which field starts */
    uint16 len;               /* Length of run in buffer */
  } fieldData[HPUF_MAX];
} HttpParserUrl;

/* http parser data */
class HttpParserData {
	friend class HttpParser;

public:
	HttpParserData();
	~HttpParserData();

	HttpParserType getParserType();
	HttpParserType getParserType() const;
	uint16 getHttpMajor();
	uint16 getHttpMajor() const;
	uint16 getHttpMinor();
	uint16 getHttpMinor() const;
	uint32 getStatusCode();
	uint32 getStatusCode() const;
	HttpMethod getHttpMethod(); 
	HttpMethod getHttpMethod() const;
	HttpParserErrno getParserErrno();
	HttpParserErrno getParserErrno() const;
	void* getUserData();
	void* getUserData() const;

private:
	HttpParserData(const HttpParserData&);
	HttpParserData& operator=(const HttpParserData&);

private:
	/** PRIVATE **/
	uint32 m_type : 2;        /* enum HttpParserType */
	uint32 m_flags : 7;       /* values from enum HttpParserFlag */
	uint32 m_state : 7;       /* enum ParserState */
	uint32 m_headerState : 8; /* enum HeaderStates */
	uint32 m_index : 8;       /* index into current matcher */

	uint32 m_nread;           /* # bytes read in various scenarios */
	uint64 m_contentLength;   /* # bytes in body (0 if no Content-Length header) */

	/** READ-ONLY **/
	uint16 m_httpMajor;
	uint16 m_httpMinor;
	uint32 m_statusCode : 16; /* responses only */
	uint32 m_method : 8;      /* requests only */
	uint32 m_httpErrno : 7;

	/* 1 = Upgrade header was present and the parser has exited because of that.
	* 0 = No upgrade header present.
	* Should be checked when http_parser_execute() returns in addition to
	* error checking.
	 ---------------*/
	uint32 m_upgrade : 1;
	void* m_data;	/* user data */
};

/**
 * @brief http parser listener
 */
class HttpParserListener {
public:
	virtual ~HttpParserListener() {}
	virtual int32 onMessageBegin(HttpParserData* data) = 0;
	virtual int32 onUrl(HttpParserData* data, const uint8* url, int32 len) = 0;
	virtual int32 onStatus(HttpParserData* data, const uint8* status, int32 len) = 0;
	virtual int32 onHeaderField(HttpParserData* data, const uint8* headerField, int32 len) = 0;
	virtual int32 onHeaderValue(HttpParserData* data, const uint8* headerValue, int32 len) = 0;
	virtual int32 onHeadersComplete(HttpParserData* data) = 0;
	virtual int32 onBody(HttpParserData* data, const uint8* body, int32 len) = 0;
	virtual int32 onMessageComplete(HttpParserData* data) = 0;
};

/**
 * @brief http parser
 */
class HttpParser {
private:
	typedef std::list<HttpParserListener*> HttpParserListenerLst;

	typedef enum etag_ParserState { 
	S_DEAD = 1 /* important that this is > 0 */,

	S_START_REQ_OR_RES, 
	S_RES_OR_RESP_H,
	S_START_RES,
	S_RES_H,
	S_RES_HT,
	S_RES_HTT,
	S_RES_HTTP,
	S_RES_FIRST_HTTP_MAJOR,
	S_RES_HTTP_MAJOR,
	S_RES_FIRST_HTTP_MINOR,
	S_RES_HTTP_MINOR,
	S_RES_FIRST_STATUS_CODE,
	S_RES_STATUS_CODE,
	S_RES_STATUS_START,
	S_RES_STATUS,
	S_RES_LINE_ALMOST_DONE, 
	
	S_START_REQ,
	
	S_REQ_METHOD,
	S_REQ_SPACES_BEFORE_URL,
	S_REQ_SCHEMA,
	S_REQ_SCHEMA_SLASH,
	S_REQ_SCHEMA_SLASH_SLASH,
	S_REQ_SERVER_START,
	S_REQ_SERVER,
	S_REQ_SERVER_WITH_AT,
	S_REQ_PATH,
	S_REQ_QUERY_STRING_START,
	S_REQ_QUERY_STRING,
	S_REQ_FRAGMENT_START,
	S_REQ_FRAGMENT,
	S_REQ_HTTP_START,
	S_REQ_HTTP_H,
	S_REQ_HTTP_HT,
	S_REQ_HTTP_HTT,
	S_REQ_HTTP_HTTP,
	S_REQ_FIRST_HTTP_MAJOR,
	S_REQ_HTTP_MAJOR,
	S_REQ_FIRST_HTTP_MINOR,
	S_REQ_HTTP_MINOR,
	S_REQ_LINE_ALMOST_DONE,
	
	S_HEADER_FIELD_START,
	S_HEADER_FIELD,
	S_HEADER_VALUE_DISCARD_WS,
	S_HEADER_VALUE_DISCARD_WS_ALMOST_DONE,
	S_HEADER_VALUE_DISCARD_LWS,
	S_HEADER_VALUE_START,
	S_HEADER_VALUE,
	S_HEADER_VALUE_LWS,
	
	S_HEADER_ALMOST_DONE,
	
	S_CHUNK_SIZE_START,
	S_CHUNK_SIZE,
	S_CHUNK_PARAMETERS,
	S_CHUNK_SIZE_ALMOST_DONE,
	
	S_HEADERS_ALMOST_DONE,
	S_HEADERS_DONE,

	/* Important: 's_headers_done' must be the last 'header' state. All
	* states beyond this must be 'body' states. It is used for overflow
	* checking. See the PARSING_HEADER() macro.
	*/

	S_CHUNK_DATA,
	S_CHUNK_DATA_ALMOST_DONE,
	S_CHUNK_DATA_DONE,
	
	S_BODY_IDENTITY,
	S_BODY_IDENTITY_EOF,
	
	S_MESSAGE_DONE
} ParserState;

typedef enum etag_HeaderStates { 
	H_GENERAL = 0,
	H_C,
	H_CO,
	H_CON,

	H_MATCHING_CONNECTION,
	H_MATCHING_PROXY_CONNECTION,
	H_MATCHING_CONTENT_LENGTH,
	H_MATCHING_TRANSFER_ENCODING,
	H_MATCHING_UPGRADE,

	H_CONNECTION,
	H_CONTENT_LENGTH,
	H_TRANSFER_ENCODING,
	H_UPGRADE,

	H_MATCHING_TRANSFER_ENCODING_CHUNKED, 
	H_MATCHING_CONNECTION_TOKEN_START,
	H_MATCHING_CONNECTION_KEEP_ALIVE,
	H_MATCHING_CONNECTION_CLOSE,
	H_MATCHING_CONNECTION_UPGRADE,
	H_MATCHING_CONNECTION_TOKEN,
	H_TRANSFER_ENCODING_CHUNKED,
	H_CONNECTION_KEEP_ALIVE,
	H_CONNECTION_CLOSE,
	H_CONNECTION_UPGRADE
} HeaderStates;

typedef enum etag_HttpHostState {
	S_HTTP_HOST_DEAD = 1,
	S_HTTP_USERINFO_START,
	S_HTTP_USERINFO,
	S_HTTP_HOST_START,
	S_HTTP_HOST_V6_START,
	S_HTTP_HOST,
	S_HTTP_HOST_V6,
	S_HTTP_HOST_V6_END,
	S_HTTP_HOST_PORT_START,
	S_HTTP_HOST_PORT
} HttpHostState;

public:
	HttpParser();
	~HttpParser();

	int32 parse(const uint8* data, int32 len);

	void reset(HttpParserType type, void* data = NULL/* user data */);

	bool addListener(HttpParserListener* listener);
	void delListener(HttpParserListener* listener);

	static std::string getMethodStr(HttpMethod m);
	static std::string getErrnoName(HttpParserErrno err);
	static std::string getErrnoDescription(HttpParserErrno err);

	const HttpParserData& getHttpParserData() const;

	bool isBodyFinal();

	static int32 parseUrl(const uint8* buf, int32 buflen, bool isConnect, HttpParserUrl& u);

private:
	ParserState newMessage(const HttpParserData& data);
	bool isKeepAlive(const HttpParserData& data);
	bool isNeedEof(const HttpParserData& data);

	static ParserState parseUrlChar(ParserState s, const char ch);

	static bool strictCheck(bool b);
	static bool isUserInfoChar(uint8 ch);
	static bool isUrlChar(uint8 ch);
	static bool isHostChar(uint8 ch);

	static uint8 token(uint8 ch);
	static uint8 strcitToken(uint8 ch);

	static uint8 lower(uint8 ch);

	static HttpHostState parseHostChar(HttpHostState s,	const uint8 ch);
	static int32 parseHost(const uint8* buf, HttpParserUrl& u, bool foundAt);

protected:
	HttpParserData m_data;
	HttpParserListenerLst m_listenerLst;
};

}
